package org.example;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



public class ApplicationContext {

    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap();
    private Map<String,Object> singletonObjects = new HashMap();//单例池，一级缓存
    private Map<String, Object> earlySingletonObjects = new HashMap<>(); // 二级缓存
    private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(); // 三级缓存
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public ApplicationContext(Class configClass){

        //Bean扫描
        scan(configClass);

        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();

            if (BeanPostProcessor.class.isAssignableFrom(beanDefinition.getType())){
                beanPostProcessors.add((BeanPostProcessor) createBean(beanName,beanDefinition));
            }
        }

        //非懒加载的单例bean
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();

            if (beanDefinition.getScope().equals("singleton") && beanDefinition.getLazy().equals(false)){
                //创建bean
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }

    }

    //bean的创建的生命周期
    public Object createBean(String beanName,BeanDefinition beanDefinition){
        Class<?> type = beanDefinition.getType();

        try {

            //实例化前

            //实例化，推断构造方法
            Object instance = type.getDeclaredConstructor().newInstance();

            //实例化后
            //将工厂对象加入三级缓存（暴露早期引用）
            final Object finalInstance = instance;
            singletonFactories.put(beanName, () -> finalInstance);

            //依赖注入  填充bean 依赖查找,简单实现依赖注入Autowired
            for (Field field : instance.getClass().getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)){
                    String name = field.getName();
                    field.setAccessible(true);
                    field.set(instance,getBean(name));
                }
            }

/*            //初始化前  @PostConstruct
            for (Method method : instance.getClass().getDeclaredMethods()) {
                if (method.isAnnotationPresent(PostConstruct.class)){
                    method.invoke(instance);
                }
            }*/
            //初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors){
                instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }

            //初始化 InitializingBean
            if (instance instanceof InitializingBean initializingBean){
                initializingBean.afterPropertiesSet();
            }

            //初始化后 AOP BeanPostProcessor
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors){
                instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }

            //初始化完成，移出二级和三级缓存，存入一级缓存
            singletonObjects.put(beanName,instance);
            earlySingletonObjects.remove(beanName);
            singletonFactories.remove(beanName);


            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)){
            //解析配置类  @Bean,@Import
            //Bean扫描
            //扫描路径，包路径
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            path = path.replace(".","/");

            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);

            try {
                File file = new File(resource.toURI());
                if (file.isDirectory()){
                    for (File listFile : file.listFiles()){
                        String filePath = listFile.getPath();
                        filePath = filePath.substring(filePath.indexOf("org"),filePath.indexOf(".class"));
                        filePath = filePath.replace("\\",".");
                        //System.out.println(filePath);

                        Class<?> Clazz = classLoader.loadClass(filePath);//加载类

                        if (Clazz.isAnnotationPresent(Component.class)){
                            //BeanDefinition
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(Clazz);
                            if (Clazz.isAnnotationPresent(Lazy.class)){
                                beanDefinition.setLazy(true);
                            }else {
                                beanDefinition.setLazy(false);
                            }
                            if (Clazz.isAnnotationPresent(Scope.class)){
                                beanDefinition.setScope(Clazz.getAnnotation(Scope.class).value());
                            }else {
                                beanDefinition.setScope("singleton");
                            }

                            String beanName = Clazz.getAnnotation(Component.class).value();
                            // 如果没有指定 bean 名称，则使用类名的小写形式作为默认名称
                            if (beanName.equals("")){
                                beanName = Clazz.getSimpleName().substring(0,1).toLowerCase()+Clazz.getSimpleName().substring(1);
                                //System.out.println(beanName);
                            }
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    public Object getBean(String beanname){

        if (!beanDefinitionMap.containsKey(beanname)){
            throw new NullPointerException("Bean '" + beanname + "' not found");
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanname);

        if (beanDefinition.getScope().equals("prototype")){
            //创建bean
            return createBean(beanname,beanDefinition);
        }else {
            // 单例模式：依次检查一级、二级、三级缓存
            Object bean = singletonObjects.get(beanname);
            if (bean != null){
                return bean;
            }

            bean = earlySingletonObjects.get(beanname);
            if (bean != null){
                return bean;
            }

            ObjectFactory<?> factory = singletonFactories.get(beanname);
            if (factory != null){
                // 从三级缓存获取早期对象，并迁移到二级缓存
                bean = factory.getObject();
                earlySingletonObjects.put(beanname, bean);
                singletonObjects.remove(beanname);
                return bean;
            }

            // 没有缓存，创建Bean并最终存入一级缓存
            bean = createBean(beanname,beanDefinition);
            singletonObjects.put(beanname,bean);
            return bean;
        }
    }
}
